package com.xyy.saas.payment.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.core.codec.Base64;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.xyy.ec.base.framework.rpc.ApiRPCResult;
import com.xyy.ec.order.api.query.OrderApi;

import com.xyy.ec.order.dto.pay.ABChinaBillDto;
import com.xyy.ec.order.dto.pay.ABChinaBusinessDto;
import com.xyy.ec.order.dto.pay.ABChinaTradeInfoDto;
import com.xyy.saas.payment.adpater.abchinaLoan.core.req.OrderStatusNotifyReq;
import com.xyy.saas.payment.adpater.abchinaLoan.service.AbchinaLoanService;
import com.xyy.saas.payment.callback.core.dto.CallbackMsg;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Builder;
import com.xyy.saas.payment.common.pool.PoolUtil;
import com.xyy.saas.payment.cores.constants.CallbackConstants;
import com.xyy.saas.payment.cores.enums.AbchinaLoanStatusEnum;
import com.xyy.saas.payment.cores.enums.FinanceChannelEnum;
import com.xyy.saas.payment.cores.enums.PinganLoanApplyStatusEnum;
import com.xyy.saas.payment.cores.vo.AbchinaLoanNotifyApplyStatusVo;
import com.xyy.saas.payment.dao.model.PayFinanceApply;
import com.xyy.saas.payment.service.PinganLoanFinanceApplyService;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.util.ABChinaUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * 农行链e贷
 *
 * @author wangzeqi
 */
@Slf4j
@RestController
@RequestMapping("/api/abchinaLoan")
public class AbchinaLoanController {

    @Reference(version = "1.0.0")
    private OrderApi orderApi;
    @Resource
    private CallbackRunner callbackRunner;
    @Resource
    private PinganLoanFinanceApplyService pinganLoanFinanceApplyService;

    @Resource
    private AbchinaLoanService abchinaLoanService;
    @Autowired
    private AlarmService alarmService;

    /**
     * 贷款申请状态通知
     */
    @PostMapping(value = "/notify/applyStatus/v1")
    public void notifyApplyStatus(HttpServletRequest request, HttpServletResponse response) {
        String alarmParam = "";
        try {
            String jsonStr = ABChinaUtil.decrypt(request);
            log.info("AbchinaLoanController notifyApplyStatus decrypt json:{}", jsonStr);
            final AbchinaLoanNotifyApplyStatusVo abchinaLoanNotifyApplyStatusVo = JSONObject.parseObject(jsonStr, AbchinaLoanNotifyApplyStatusVo.class);
            if (abchinaLoanNotifyApplyStatusVo == null || StringUtils.isBlank(abchinaLoanNotifyApplyStatusVo.getUscc()) ||
                    StringUtils.isBlank(abchinaLoanNotifyApplyStatusVo.getLoanStatus())) {
                makeNotifyApplyStatusResponse(response, "-1", "参数为空");
                return;
            }
            alarmParam = abchinaLoanNotifyApplyStatusVo.getUscc();
            final PinganLoanApplyStatusEnum applyStatusEnum = AbchinaLoanStatusEnum.getPinganLoanApplyStatusEnum(abchinaLoanNotifyApplyStatusVo.getLoanStatus());
            if (applyStatusEnum == null) {
                log.error("AbchinaLoanController notifyApplyStatus loanStatus inconsistent. param:{}", JSON.toJSONString(abchinaLoanNotifyApplyStatusVo));
                makeNotifyApplyStatusResponse(response, "-1", "回调状态未知");
                return;
            }
            PayFinanceApply payFinanceApply = pinganLoanFinanceApplyService.findLatestRecordByLoanBalanceNo(abchinaLoanNotifyApplyStatusVo.getUscc(), FinanceChannelEnum.ABCHINA_LOAN.getCode());
            if (payFinanceApply == null) {
                log.error("AbchinaLoanController notifyApplyStatus payFinanceApply is null, param:{}", JSON.toJSONString(abchinaLoanNotifyApplyStatusVo));
                makeNotifyApplyStatusResponse(response, "-1", "外部企业统一信用代码在业务侧不存在");
                return;
            }
            payFinanceApply.setApplyStatus(applyStatusEnum.getCode());
            pinganLoanFinanceApplyService.updateByPrimaryKeySelective(payFinanceApply);
            // 回调ec
            abchinaLoanNotifyApplyStatusVo.setUserId(payFinanceApply.getBusinessUserId());
            abchinaLoanNotifyApplyStatusVo.setApplyStatus(applyStatusEnum);
            CallbackMsg callbackMsg = Builder.of(CallbackMsg::new)
                    .with(CallbackMsg::setCallback, CallbackConstants.ABCHINA_LOAN_CALLBACK_GROUP)
                    .with(CallbackMsg::setType, CallbackConstants.ABCHINA_LOAN_CALLBACK_APPLYSTATUS)
                    .with(CallbackMsg::setExtend, JSON.toJSONString(abchinaLoanNotifyApplyStatusVo))
                    .build();
            callbackRunner.callback(callbackMsg);

            makeNotifyApplyStatusResponse(response, "0", "成功");
        } catch (Exception e) {
            log.error("AbchinaLoanController notifyApplyStatus error, uscc:{}", alarmParam, e);
            alarmService.asyncAlarm("农行贷款申请状态通知异常", "uscc:" + alarmParam);
        }
    }

    @SneakyThrows
    private void makeNotifyApplyStatusResponse(HttpServletResponse response, String code, String msg) {
        Map<String, Object> respMap = new HashMap<>();
        respMap.put("resCode", code);
        respMap.put("resMsg", msg);
        ABChinaUtil.encrypt(respMap, response);
    }

    @PostMapping(value = "/queryTradeInfo")
    public void queryABChinaTradeInfo(HttpServletRequest request, HttpServletResponse response) {
        String alarmParam = "";
        try {
            String jsonStr = ABChinaUtil.decrypt(request);
            log.info("queryTradeInfo decrypt json:{}", jsonStr);

            String uscc = (String) JSON.parseObject(jsonStr).get("uscc");
            alarmParam = uscc;
            ApiRPCResult<ABChinaTradeInfoDto> apiRPCResult = orderApi.queryABChinaTradeInfo(uscc);
            Map<String, Object> respMap;
            if (apiRPCResult.isSuccess()) {
                respMap = buildSuccessResponse(apiRPCResult.getData());
            } else {
                respMap = buildFailedResponse(apiRPCResult.getMsg());
            }

            // 加密
            ABChinaUtil.encrypt(respMap, response);
        } catch (Exception e) {
            log.error("queryABChinaTradeInfo#error:{}, uscc:{}", e.getMessage(), alarmParam, e);
            alarmService.asyncAlarm("农行查询商户交易信息异常", "uscc:" + alarmParam);
        }
    }

    @PostMapping(value = "/queryBusinessInfo")
    public void queryBusinessInfo(HttpServletRequest request, HttpServletResponse response) {
        String alarmParam = "";
        try {
            String jsonStr = ABChinaUtil.decrypt(request);
            log.info("queryBusinessInfo decrypt json:{}", jsonStr);

            String uscc = (String) JSON.parseObject(jsonStr).get("uscc");
            alarmParam = uscc;
            ApiRPCResult<ABChinaBusinessDto> apiRPCResult = orderApi.queryABChinaBusinessInfo(uscc);
            Map<String, Object> respMap;
            if (apiRPCResult.isSuccess()) {
                respMap = buildSuccessResponse(transformBusinessDto(apiRPCResult.getData()));
            } else {
                respMap = buildFailedResponse(apiRPCResult.getMsg());
            }

            // 图片不加密
            response.setCharacterEncoding("utf-8");
            Map<String, String> result = new HashMap<>(1);
            result.put("encryReqData", JSON.toJSONString(respMap));
            PrintWriter writer = response.getWriter();
            writer.write(JSON.toJSONString(result));
            writer.close();
        } catch (Exception e) {
            log.error("queryABChinaTradeInfo#error:{}, uscc:{}", e.getMessage(), alarmParam, e);
            alarmService.asyncAlarm("农行查询商户资质信息异常", "uscc:" + alarmParam);
        }
    }

    @PostMapping(value = "/queryBill")
    public void queryBill(HttpServletRequest request, HttpServletResponse response) {
        String alarmParam = "";
        try {
            String jsonStr = ABChinaUtil.decrypt(request);
            log.info("queryBill decrypt json:{}", jsonStr);
            JSONObject jsonObject = JSON.parseObject(jsonStr);
            String orderId = (String) jsonObject.get("orderId");
            Integer queryType = (Integer) jsonObject.get("queryType");

            alarmParam = orderId;

            Map<String, Object> respMap;
            if (StringUtils.isEmpty(orderId) || queryType == null) {
                respMap = buildFailedResponse("订单号和查询类型不能为空");
            } else {
                ApiRPCResult<ABChinaBillDto> apiRPCResult = orderApi.queryABChinaBill(orderId, queryType);
                if (apiRPCResult.isSuccess()) {
                    respMap = buildSuccessResponse(apiRPCResult.getData());
                } else {
                    respMap = buildFailedResponse(apiRPCResult.getMsg());
                }
            }

            // 加密
            ABChinaUtil.encrypt(respMap, response);
        } catch (Exception e) {
            log.error("queryBill#error:{}, orderId:{}", e.getMessage(), alarmParam, e);
        }
    }

    @PostMapping(value = "/orderStatusNotify")
    public void orderStatusNotify(HttpServletRequest request, HttpServletResponse response) {
        String alarmParam = "";
        try {
            String jsonStr = ABChinaUtil.decrypt(request);
            log.info("orderStatusNotify decrypt json:{}", jsonStr);
            OrderStatusNotifyReq notifyReq = JSON.parseObject(jsonStr, OrderStatusNotifyReq.class);
            alarmParam = notifyReq.getOrderId();

            Map<String, Object> respMap;
            if (!notifyReq.checkParam()) {
                respMap = buildFailedResponse("参数异常");
            } else {
                ApiRPCResult apiRPCResult = abchinaLoanService.orderStatusNotify(notifyReq);
                if (apiRPCResult.isSuccess()) {
                    respMap = buildSuccessResponse(null);
                } else {
                    respMap = buildFailedResponse(apiRPCResult.getMsg());
                }
            }

            // 加密
            ABChinaUtil.encrypt(respMap, response);
        } catch (Exception e) {
            log.error("orderStatusNotify#error:{}, orderId:{}", e.getMessage(), alarmParam, e);
            alarmService.asyncAlarm("农行订单状态通知异常", "orderId:" + alarmParam);
        }
    }

    private ABChinaBusinessDto transformBusinessDto(ABChinaBusinessDto businessDto) {
        ABChinaBusinessDto resultDto = new ABChinaBusinessDto();
        if (businessDto == null) {
            return resultDto;
        }

        // 异步，降低接口响应时间
        List<CompletableFuture<String>> certFutureList = asyncResizeAndBase64(businessDto.getCertFileObj());
        List<CompletableFuture<String>> usccFutureList = asyncResizeAndBase64(businessDto.getUsccFileObj());

        List<String> certList = certFutureList.stream().map(f -> PoolUtil.sneakyGet(f)).collect(Collectors.toList());
        List<String> usccList = usccFutureList.stream().map(f -> PoolUtil.sneakyGet(f)).collect(Collectors.toList());
        resultDto.setCertFileObj(certList);
        resultDto.setUsccFileObj(usccList);
        return resultDto;
    }

    private List<CompletableFuture<String>> asyncResizeAndBase64(List<String> urls) {
        if (CollUtil.isEmpty(urls)) {
            return Collections.emptyList();
        }
        // 最多取5张
        int len = Math.min(urls.size(), 3);
        List<CompletableFuture<String>> futureList = new ArrayList<>(len);
        for (int i = 0; i < len; i++) {
            String url = urls.get(i);

            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                byte[] bytes = HttpUtil.createGet(url, true)
                        // 防止拉垮接口
                        .timeout(1500)
                        .execute().bodyBytes();
                return ImgUtil.read(new ByteArrayInputStream(bytes));
            }, PoolUtil.IO_POLL).thenApplyAsync(image -> {
                Image scale = ImgUtil.scale(image, 0.5f);
                // 保存缩放后的图片
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                ImgUtil.write(scale, "JPEG", outputStream);
                return Base64.encode(outputStream.toByteArray());
            }, PoolUtil.IO_POLL);
            futureList.add(future);
        }
        return futureList;
    }

    private Map<String, Object> buildSuccessResponse(Object data) {
        Map<String, Object> respMap = new HashMap<>(3);
        respMap.put("resCode", "0");
        respMap.put("resMsg", "成功");
        respMap.put("data", data);
        return respMap;
    }

    private Map<String, Object> buildFailedResponse(String errorMsg) {
        Map<String, Object> respMap = new HashMap<>(2);
        respMap.put("resCode", "-1");
        respMap.put("resMsg", errorMsg);
        return respMap;
    }
}
